local t_util = require "libs/tableutil"

local c_util = {}

-- 获取两个实体的方向
function c_util:GetDirection(source, target, dire)
    if dire == "left" or dire == "right" then
        local tmp = {
            x = target.x,
            z = target.z
        }
        target.x = source.z - tmp.z + source.x
        target.z = tmp.x - source.x + source.z
    end
    local numy = target.z - source.z
    local numx = target.x - source.x
    local absx = math.abs(numx)
    local absy = math.abs(numy)
    if absx == 0 and absy == 0 then
        return 0.5, 0.5
    end
    if absx > absy then
        numx = numx / absx
        numy = numy / absx
    else
        numx = numx / absy
        numy = numy / absy
    end
    if dire == "down" or dire == "right" then
        return -numx / 2, -numy / 2
    else
        return numx / 2, numy / 2
    end
end

-- 获取圆心与圆外一点的连线与圆的交点
function c_util:GetIntersectPotRadiusPot(pot1, radius, pot2)
    local dx = pot2.x - pot1.x
    local dz = pot2.z - pot1.z
    local d = math.sqrt(dx * dx + dz * dz)
    if d == 0 then
        return pot1
    else
        return Vector3(pot1.x + dx * radius / d, 0, pot1.z + dz * radius / d)
    end
end

-- 判断str是否包含item
function c_util:IsStrContains(str, item)
    -- 将两个字符串都转换为小写，并去除空格
    str = string.lower(str):gsub("%s+", "")
    item = string.lower(item):gsub("%s+", "")
    -- 判断str是否包含item，返回布尔值
    return string.find(str, item) ~= nil
end

-- 获取两点间距离
function c_util:GetDist(x1, y1, x2, y2)
    return math.sqrt(self:GetDistWithoutSqrt(x1, y1, x2, y2))
end
-- 某些情况下使用，减少一点点运算
function c_util:GetDistWithoutSqrt(x1, y1, x2, y2)
    return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)
end


-- 比较两个克隆体的属性,没啥好方法啊
function c_util:GreaterComn(A, B, compname, infoname, default)
    default = default or 0
    local comA = A.components[compname]
    local comB = B.components[compname]
    local numA = comA and type(comA[infoname]) == "number" and comA[infoname] or default
    local numB = comB and type(comB[infoname]) == "number" and comB[infoname] or default
    -- print(infoname, numA, numB)
    return numA >= numB
end

function c_util:GreaterTags(A, B, tags)
    local numA = t_util:GetElement(tags, function(_, tag)
        return table.contains(A.tags, tag)
    end) and 1 or 0
    local numB = t_util:GetElement(tags, function(_, tag)
        return table.contains(B.tags, tag)
    end) and 1 or 0
    return numA >= numB
end
-- 生成位置id
function c_util:GetPosID(x, y)
    return string.format("%.2f_%.2f", x, y)
end
function c_util:GetStardPos(x, y)
    if x and y then
        return string.format("%.2f", x), string.format("%.2f", y)
    end
end
-- unique会去除重复的资源点（其实加载时去除最好了，这里去除主要是懒）
local function toBeUnique(pos_list)
    local _pos_list = {}
    t_util:IPairs(pos_list, function(pos)
        _pos_list[c_util:GetPosID(pos.x, pos.y)] = pos
    end)
    pos_list = {}
    t_util:Pairs(_pos_list, function(_, pos)
        table.insert(pos_list, pos)
    end)
    return pos_list
end
-- 自下而上的层次聚合算法
function c_util:Commu(pos_list, range, unique)
    range = range or 0
    -- unique会去除重复的资源点（其实加载时去除最好了，这里去除主要是懒）
    pos_list = unique and toBeUnique(pos_list) or pos_list
    local count = #pos_list
    for i = 1, count do
        local pos1 = pos_list[i]
        if not pos1.id then
            pos1.id = self:GetPosID(pos1.x, pos1.y)
        end
        for j = i + 1, count do
            local pos2 = pos_list[j]
            if self:GetDist(pos1.x, pos1.y, pos2.x, pos2.y) < range then
                if not pos2.id then
                    pos2.id = pos1.id
                else
                    t_util:IPairs(pos_list, function(pos3)
                        if pos3.id == pos2.id then
                            pos3.id = pos1.id
                        end
                    end)
                end
            end
        end
    end
    local commus = {}
    t_util:IPairs(pos_list, function(pos)
        if not commus[pos.id] then
            commus[pos.id] = {pos}
        else
            table.insert(commus[pos.id], pos)
        end
    end)
    return commus
end

function c_util:isRectanglesIntersecting(A, B)  
    return not (A.x2 < B.x1 or A.x1 > B.x2 or A.y2 < B.y1 or A.y1 > B.y2)  
end

function c_util:CommusRec(pos_list, range, unique)
    range = range or 0
    pos_list = unique and toBeUnique(pos_list) or pos_list
    local commus = {}
    t_util:IPairs(pos_list, function(pos)
        local x,y = pos.x, pos.y
        local in_commu = t_util:GetElement(commus, function(commu_id, commu)
            if x > commu.x1 and x < commu.x2 and y > commu.y1 and y < commu.y2 then
                commu.x1 = x - range < commu.x1 and x - range or commu.x1 
                commu.y1 = y - range < commu.y1 and y - range or commu.y1
                commu.x2 = x + range > commu.x2 and x + range or commu.x2
                commu.y2 = y + range > commu.y2 and y + range or commu.y2 
                table.insert(commu.list, pos)
                return commu
            end
        end) 
        if in_commu then
            t_util:GetElement(commus, function(commu_id, commu)
                if commu == in_commu then return end
                if self:isRectanglesIntersecting(in_commu, commu)then
                    in_commu.x1 = in_commu.x1 < commu.x1 and in_commu.x1 or commu.x1
                    in_commu.y1 = in_commu.y1 < commu.y1 and in_commu.y1 or commu.y1
                    in_commu.x2 = in_commu.x2 > commu.x2 and in_commu.x2 or commu.x2
                    in_commu.y2 = in_commu.y2 > commu.y2 and in_commu.y2 or commu.y2
                    t_util:IPairs(commu.list, function(pos)
                        table.insert(in_commu.list, pos)
                    end)
                    commus[commu_id] = nil
                    return true
                end
            end)
        else
            table.insert(commus, {
                x1 = x - range,
                x2 = x + range,
                y1 = y - range,
                y2 = y + range,
                list = {pos}
            })
        end
    end)
    return commus
end

-- 矩形聚合算法，理论速度应该远胜上面的方法，缺点是只适合构造成矩形的群落
function c_util:CommuRec(pos_list, range, unique)
    local commus = self:CommusRec(pos_list, range, unique)
    local comm = {}
    t_util:Pairs(commus, function(_, commu)
        table.insert(comm, commu.list)
    end)
    return comm
end

-- 包裹算法，不准但快
function c_util:GetCenterPos(commus)
    local pos_list = {}
    t_util:Pairs(commus, function(_, commu)
        local xmin,xmax,ymin,ymax
        t_util:IPairs(commu, function(pos)
            local px, py = pos.x, pos.y
            if not xmin then
                xmin,xmax,ymin,ymax = px,px,py,py
            else
                if px<xmin then
                    xmin = px
                elseif px>xmax then
                    xmax = px
                end
                if py<ymin then
                    ymin = py
                elseif py>ymax then
                    ymax = py
                end
            end
        end)
        table.insert(pos_list, {
            x = (xmin+xmax)/2,
            y = (ymin+ymax)/2
        })
    end)
    return pos_list
end

function c_util:ModeToXY(mode, mx, my, px, py)
    mx = (mode < 3 or mode > 6) and mx or -mx
    my = (mode % 2 == 0) and my or -my
    if mode > 4 then
        mx, my = my, mx
    end
    mx, my = px + mx, py + my
    return mx, my
end


function c_util:PosToAngle(pos)
	local angle = math.atan2(pos.x, pos.z) * RADIANS + 270
	if angle < 0 then
		angle = 360 + angle
    elseif angle > 360 then
        angle = angle - 360
	end
	return angle
end


return c_util
